package controllers

import (
	"../../../deps/lessgo/pagelet"
	"../../../deps/lessgo/utils"
	"../../../deps/lessgo/utilx"
	"../../base"
	"../../store"
	//"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
)

var (
	bucketNamePattern  = regexp.MustCompile("^[a-z]{1}[a-z0-9]{3,20}$")
	bucketEmailPattern = regexp.MustCompile("^[_a-z0-9-]+(\\.[_a-z0-9-]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*(\\.[a-z]{2,10})$")
)

type Bucket struct {
	*pagelet.Controller
}

func (c Bucket) SetupAction() {

	c.AutoRender = false

	var rsp struct {
		ResponseJson
	}
	rsp.ApiVersion = apiVersion
	rsp.Status = 400
	rsp.Message = "Bad Request"

	defer func() {
		if rspj, err := utils.JsonEncode(rsp); err == nil {
			io.WriteString(c.Response.Out, rspj)
		}
	}()

	body := string(c.Request.RawBody)
	if body == "" {
		return
	}

	var req struct {
		AccessToken string `json:"access_token"`
		Data        struct {
			base.Bucket
			AutoAccessKey string `json:"auto_access_key"`
		} `json:"data"`
	}
	err := utils.JsonDecode(body, &req)
	if err != nil {
		rsp.Message = err.Error()
		return
	}
	if req.AccessToken == "" {
		return
	} else {
		// TODO
	}

	//
	var preBucket base.Bucket
	preRs, err := store.NodeGet("/sys/bs/" + req.Data.Name)
	if err == nil {
		_ = preRs.ReplyJson(&preBucket)
	}

	if preBucket.Uuid == "" {
		req.Data.Uuid = utils.StringNewRand36(32)
	} else {
		req.Data.Uuid = preBucket.Uuid
	}

	req.Data.Name = strings.TrimSpace(strings.ToLower(req.Data.Name))
	if preBucket.Name == "" {

		if len(req.Data.Name) < 6 || len(req.Data.Name) > 20 {
			rsp.Message = "Bucket Name must be between 6 and 20 characters long"
			rsp.Status = 400
			return
		} else if mat := bucketNamePattern.MatchString(req.Data.Name); !mat {
			rsp.Message = "Bucket Name must consist of letters or numbers, and begin with a letter"
			rsp.Status = 400
			return
		}
	}

	req.Data.Desc = strings.TrimSpace(req.Data.Desc)
	if req.Data.Desc == "" {
		rsp.Message = "Description can not be null"
		rsp.Status = 400
		return
	}
	if len(req.Data.Desc) > 100 {
		req.Data.Desc = req.Data.Desc[:100]
	}

	if req.Data.DataSize < 1 {
		req.Data.DataSize = 1
	} else if req.Data.DataSize > 1000 {
		req.Data.DataSize = 1000
	}

	req.Data.OwnerName = strings.TrimSpace(req.Data.OwnerName)
	if len(req.Data.OwnerName) > 40 {
		req.Data.OwnerName = req.Data.OwnerName[:40]
	}

	req.Data.OwnerEmail = strings.TrimSpace(strings.ToLower(req.Data.OwnerEmail))
	if len(req.Data.OwnerEmail) > 0 {
		if len(req.Data.OwnerEmail) > 100 {
			rsp.Message = "Email is not valid"
			rsp.Status = 400
			return
		}
		if mat := bucketEmailPattern.MatchString(req.Data.OwnerEmail); !mat {
			rsp.Message = "Email is not valid"
			rsp.Status = 400
			return
		}
	}

	if len(preBucket.Created) > 8 {
		req.Data.Created = preBucket.Created
	} else {
		req.Data.Created = utilx.TimeNow("atom")
	}
	req.Data.Updated = utilx.TimeNow("atom")

	//
	bucketjs, _ := utils.JsonEncode(req.Data)

	ver, _ := store.Cmd("INCRBY", "sys:keeper:ver", 1).Int64()
	node := store.NodeProposal{
		Key:    "/sys/bs/" + req.Data.Name,
		Val:    bucketjs,
		VerSet: uint64(ver),
	}
	store.NodeSet(&node) // TODO

	if req.Data.AutoAccessKey == "1" && preBucket.Name == "" {

		ak := base.BucketAccessKey{
			Id:      utils.StringNewRand36(16),
			Secret:  utils.StringNewRand64(32),
			Status:  1,
			Bucket:  req.Data.Name,
			Created: utilx.TimeNow("atom"),
			Updated: utilx.TimeNow("atom"),
		}
		akjs, _ := utils.JsonEncode(ak)

		ver, _ := store.Cmd("INCRBY", "sys:keeper:ver", 1).Int64()
		node = store.NodeProposal{
			Key:    "/sys/bak/" + ak.Id,
			Val:    akjs,
			VerSet: uint64(ver),
		}
		store.NodeSet(&node) // TODO

		store.Cmd("SADD", "sys:b2ak:"+req.Data.Name, ak.Id)
	}

	rsp.Status = 200
	rsp.Message = ""
}

func (c Bucket) AccessKeysAction() {

	c.AutoRender = false

	var rsp struct {
		ResponseJson
		Data []base.BucketAccessKey `json:"data"`
	}
	rsp.ApiVersion = apiVersion
	rsp.Status = 400
	rsp.Message = "Bad Request"
	rsp.Data = []base.BucketAccessKey{}

	defer func() {
		if rspj, err := utils.JsonEncode(rsp); err == nil {
			io.WriteString(c.Response.Out, rspj)
		}
	}()

	name := c.Params.Get("bucket_name")
	rsKeys, _ := store.Cmd("SMEMBERS", "sys:b2ak:"+name).List()

	for _, keyid := range rsKeys {

		var ak base.BucketAccessKey
		rs, _ := store.NodeGet("/sys/bak/" + keyid)
		if err := rs.ReplyJson(&ak); err == nil {
			rsp.Data = append(rsp.Data, ak)
		}
	}

	rsp.Status = 200
	rsp.Message = ""
}

func (c Bucket) AccessKeySetAction() {

	c.AutoRender = false

	var rsp struct {
		ResponseJson
	}
	rsp.ApiVersion = apiVersion
	rsp.Status = 400
	rsp.Message = "Bad Request"

	defer func() {
		if rspj, err := utils.JsonEncode(rsp); err == nil {
			io.WriteString(c.Response.Out, rspj)
		}
	}()

	keyid := c.Params.Get("access_key_id")

	rs, err := store.NodeGet("/sys/bak/" + keyid)
	if err != nil {
		return
	}

	var ak base.BucketAccessKey
	err = rs.ReplyJson(&ak)
	if err != nil {
		return
	}

	if c.Params.Get("status") != "" {
		ak.Status, _ = strconv.Atoi(c.Params.Get("status"))
	}

	akjs, _ := utils.JsonEncode(ak)

	ver, _ := store.Cmd("INCRBY", "sys:keeper:ver", 1).Int64()
	node := store.NodeProposal{
		Key:    "/sys/bak/" + ak.Id,
		Val:    akjs,
		VerSet: uint64(ver),
	}
	store.NodeSet(&node) // TODO

	rsp.Status = 200
	rsp.Message = ""
}

func (c Bucket) AccessKeyNewAction() {

	c.AutoRender = false

	var rsp struct {
		ResponseJson
		Data base.BucketAccessKey `json:"data"`
	}
	rsp.ApiVersion = apiVersion
	rsp.Status = 400
	rsp.Message = "Bad Request"

	defer func() {
		if rspj, err := utils.JsonEncode(rsp); err == nil {
			io.WriteString(c.Response.Out, rspj)
		}
	}()

	name := c.Params.Get("bucket_name")
	var bucket base.Bucket
	rs, err := store.NodeGet("/sys/bs/" + name)
	if err == nil {
		_ = rs.ReplyJson(&bucket)
	}
	if bucket.Uuid == "" {
		return
	}

	ak := base.BucketAccessKey{
		Id:      utils.StringNewRand36(16),
		Secret:  utils.StringNewRand64(32),
		Status:  1,
		Bucket:  bucket.Name,
		Created: utilx.TimeNow("atom"),
		Updated: utilx.TimeNow("atom"),
	}
	akjs, _ := utils.JsonEncode(ak)

	ver, _ := store.Cmd("INCRBY", "sys:keeper:ver", 1).Int64()
	node := store.NodeProposal{
		Key:    "/sys/bak/" + ak.Id,
		Val:    akjs,
		VerSet: uint64(ver),
	}
	store.NodeSet(&node) // TODO

	store.Cmd("SADD", "sys:b2ak:"+bucket.Name, ak.Id)

	rsp.Data = ak
	rsp.Status = 200
	rsp.Message = ""
}
